<?php
/**
 * 流控service，超出阈值范围外的请求直接拦截报错。
 * usage:
 *
 *
 * Created by PhpStorm.
 * User: Arvin
 * Date: 15/11/4
 * Time: 18:59
 */
class FlowController
{

    /**
     * 类实例的静态属性
     * @var Object
     */
    private static $_instance;

    /**
     * redis对象属性
     * @var null|Redis
     */
    private $redis;

    /**
     * 是否调试模式
     * @var
     */
    private $isDebug;

    /**
     * @param array $server
     */
    public function __construct($server = array())
    {
        $this->redis = $this->initRedis($server);
    }

    /**
     * 防止对象被克隆
     */
//    public function __clone()
//    {
//        throw_exception('单列对象不能被克隆');
//    }

    /**
     * 检查redis环境是否支持
     */
    private function chkRedisEVN()
    {
        if (!extension_loaded('redis')) {
            throw_exception('redis is NOT SUPPERT');    //redis扩展没添加
        }
    }

    /**
     * 获得单例redis cache 实例
     * @param array $server
     * @return null|Redis
     */
//    static public function getCacheInstance($server = array())
//    {
//        if (null === self::$_instance) {
//            self::$_instance = new self($server);
//        }
//
//        return self::$_instance;
//    }

    /**
     * 初始化redis实例
     *
     * @param $server
     * @return null|Redis
     */
    public  function initRedis($options = null)
    {
        //检测redis环境
        $this->chkRedisEVN();

        $defOption = array(
            'host' => C('REDIS_HOST') ? C('REDIS_HOST') : '127.0.0.1',
            'port' => C('REDIS_PORT') ? C('REDIS_PORT') : 6379,
            'timeout' => C('REDIS_CACHE_TIMEOUT') ? C('REDIS_CACHE_TIMEOUT') : false,
            'persistent' => false,
            'expire' => C('REDIS_CACHE_TIMEOUT'),
            'passwd' => C('REDIS_PASS'),
            'select_db' => C('REDIS_DEF_DB'),
        );
        if (!empty($options)) {
            $options = array_merge($defOption,$options);
        } else {
            $options = $defOption;
        }

        $func = $options['persistent'] ? 'pconnect' : 'connect';
        $redis = new Redis();
        $connected = $options['timeout'] === false
            ? $redis->$func($options['host'], $options['port'])
            : $redis->$func($options['host'], $options['port'], $options['timeout']);

        if ( !$redis->auth($options['passwd']) ) {
            throw_exception(L('_REDIS_AUTH_ERROR_'));
        }

        $redis->select($options['select_db']);
        return $redis;
    }

    /**
     * 是否开启调试模式
     * @param $isDebug
     */
    private function setDebug($isDebug)
    {
        $this->isDebug = $isDebug;
    }


    /**
     * 获取流控请求结果
     * @param $key key
     * @param $max 最大qps
     * @return bool
     */
    public function acquire($key, $max)
    {
        $val = $this->redis->incr($key);
        if ($val > $max) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * 释放一次请求
     * @param $key
     * @return bool
     */
    public function  release($key)
    {
        $val = $this->redis->decr($key);
        if ($val >= 0) {
            return true;
        } else {
            //如果出现负数，为了避免流量超出阈值范围，默认设置为0
            $this->redis->set($key, 0);
            return false;
        }
    }

    /**
     * 销毁一个key的控制
     * @param $key
     */
    public function  destroy($key)
    {
        return $this->redis->del($key);
    }
    
    /**
     * 添加分布式并发锁
     * @param string $lockKey 锁的键
     * @param int $expire 锁的超时时间
     * @param int $isHight 是否是高版本 如果版本<2.6.12一下就只能使用低版本的写法
     */
    public function addLock($lockKey,$expire = 2,$isHight=0)
    {
        if (intval($isHight) == 1) {
            $res = $this->redis->set($lockKey, 1, array('NX', 'EX'=>$expire));
            return $res;
        } else {
            $res = $this->redis->setnx($lockKey, 1);
            $this->redis->expire($lockKey,$expire);

            return $res;
        }
    }
    
    /**
     * 删除分布式并发锁
     * @param string $lockKey 锁的键
     */
    public function delLock($lockKey) 
    {
        $res = $this->redis->del($lockKey);
        return $res;
    }

    /**
     * 生成全局的主键值
     * @param string $pkCacheKey    主键的缓存键,根据具体业务来
     * @return int
     */
    public function createGlobalPrimaryKey($pkCacheKey)
    {
        $primaryKey = $this->redis->incr($pkCacheKey);
        return $primaryKey;
    }
}